﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using SpicIE.Common;

namespace SpicIE.Controls
{
    /// <summary>
    /// Creates a new menu item either in the tools or help menu
    /// </summary>
    public abstract class MenuItem : IControlBase, IOleCommandTarget
    {
        /// <summary>
        /// The PluginGuid _must_ be a unique GUID to identify and register the COM object correctly
        /// </summary>
        public abstract string PluginGuid { get; }
        /// <summary>
        /// The PluginProgID _must_ be provided to identify and register the COM object correctly
        /// </summary>
        public abstract string PluginProgID { get; }

        #region Member variables declaration

        protected Guid AssociatedCLSIDExtension;
        protected string MenuItemName;
        protected string MenuStatusBar;
        protected string ExecutePath;

        private Guid MenuItemID { get { return new Guid(PluginGuid); } }
        private string MenuItemProgID { get { return PluginProgID; } }

        public Guid CLSIDExtension { get { return AssociatedCLSIDExtension; } }
        public string ItemName { get { return MenuItemName; } }
        public string StatusBar { get { return MenuStatusBar; } }
        public string ExecuteScriptPath { get { return ExecutePath; } }

        /// <summary>
        /// Lets the user decide in which way the menuitem should behave. 
        /// Either opening a COMObject, or executing a script or an application
        /// </summary>
        [ComVisible(false)]
        public MenuItemCommandEnum MenuItemCommand = MenuItemCommandEnum.COMObject;

        /// <summary>
        /// Specifies wether the menuitem should appear in the tools menu or the help menu
        /// </summary>
        [ComVisible(false)]
        public MenuItemCustomEnum MenuItemCustom = MenuItemCustomEnum.Help;
        #endregion

        /// <summary>
        /// Delegate event which can be subscribed when intantiating the class
        /// </summary>
        /// <param name="value">the return value from the handle, default -1</param>
        public delegate void OLECommand(int value);
        public event OLECommand OnMenuCommand; 

        /// <summary>
        /// Specifies the type of action for the menu item 
        /// </summary>
        public enum MenuItemCommandEnum
        {
            COMObject = 1,
            Scripts = 2,
            Executables = 4
        }

        /// <summary>
        /// Specifies where the menu item should appear
        /// </summary>
        public enum MenuItemCustomEnum
        { 
            Tools = 1,
            Help = 2
        }

        #region COM object register functionality
        /// <summary>
        /// Global function for registering the com object
        /// </summary>
        public void Register()
        { 
            Host.TraceSink.TraceInformation("SpicIE: before registering MenuItem " + ItemName);

            try
            {
                string id = MenuItemID.ToString("B");

                if (ItemName == string.Empty)
                    return;

                Host.RegisterCOMObject(GetType(), MenuItemID, MenuItemProgID);

                RegistryKey mainKey = Registry.LocalMachine.CreateSubKey(Constants.REGISTRY_MENUITEM + id);

                mainKey.SetValue("CLSID", "{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}");
                mainKey.SetValue("MenuText", ItemName);
                
                if(!String.IsNullOrEmpty(StatusBar))
                    mainKey.SetValue("MenuStatusBar ", StatusBar);
                
                switch (MenuItemCustom)
                {
                    case MenuItemCustomEnum.Help:
                        mainKey.SetValue("MenuCustomize","help");
                        break;
                    case MenuItemCustomEnum.Tools:
                        mainKey.SetValue("MenuCustomize", "tools");
                        break;
                }

                switch(MenuItemCommand)
                {
                    case MenuItemCommandEnum.COMObject:
                        mainKey.SetValue("ClsidExtension", CLSIDExtension.ToString("B"));
                        break;
                    case MenuItemCommandEnum.Scripts:
                        mainKey.SetValue("Script", ExecuteScriptPath);
                        break;
                    case MenuItemCommandEnum.Executables:
                        mainKey.SetValue("Exec", ExecuteScriptPath);
                        break;
                }
            }
            catch (Exception ex)
            {
                Host.TraceSink.TraceEvent(TraceEventType.Error, 0, "Exception: Registering MenuItem " + ItemName + ": " + ex);
            }

            Host.TraceSink.TraceInformation("SpicIE: after registering MenuItem " + ItemName);
        }

        /// <summary>
        /// Global function for unregistering the com object
        /// </summary>
        public void Unregister()
        {
            Host.TraceSink.TraceInformation("SpicIE: before unregistering MenuItem " + ItemName);

            try
            {
                string id = MenuItemID.ToString("B");

                Host.UnRegisterCOMObject(MenuItemID);

                if (Registry.LocalMachine.OpenSubKey(Constants.REGISTRY_MENUITEM + id) != null)
                    Registry.LocalMachine.DeleteSubKey(Constants.REGISTRY_MENUITEM + id);
            }
            catch( Exception ex )
            {
                Host.TraceSink.TraceEvent(TraceEventType.Error, 0, "Exception: Unregistering MenuItem " + ItemName + ": " + ex);
            }

            Host.TraceSink.TraceInformation("SpicIE: after unregistering MenuItem " + ItemName);
        }
        #endregion

        #region IOLECommandTarget interface implementation
        /// <summary>
        /// interface implementation for communicating with the ole object, in this case the menuitem as com object
        /// </summary>
        /// <param name="pguidCmdGroup"></param>
        /// <param name="cCmds"></param>
        /// <param name="prgCmds"></param>
        /// <param name="pCmdText"></param>
        /// <returns>-1</returns>
        [CLSCompliant(false)]
        public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
        {
            Host.TraceSink.TraceEvent(TraceEventType.Information, 0, "MenuItem: QueryStatus " + cCmds);
            return -1;
        }

        /// <summary>
        /// interface implementation for firing the OLE Command event which can be subscribed in the intatiated class
        /// </summary>
        /// <param name="pguidCmdGroup"></param>
        /// <param name="nCmdID"></param>
        /// <param name="nCmdexecopt"></param>
        /// <param name="pvaIn"></param>
        /// <param name="pvaOut"></param>
        /// <returns>-1</returns>
        [CLSCompliant(false)]
        public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
            Host.TraceSink.TraceEvent(TraceEventType.Information, 0, "MenuItem: Exec " + nCmdID);
            if (OnMenuCommand != null) 
                OnMenuCommand(Convert.ToInt32(nCmdID));

            return -1;
        }
        #endregion
    }
}
